﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;

namespace PowerTriggers
{
    public class Program : ApplicationContext
    {
        public bool gblnAutoHide = true;

        private System.ComponentModel.IContainer components;					// a list of components to dispose when the context is disposed
        private System.Windows.Forms.NotifyIcon mainNotifyIcon;				    // the icon that sits in the system tray
        private System.Windows.Forms.ContextMenu mainNotifyIconContextMenu;	    // the context menu for the notify icon
        private System.Windows.Forms.MenuItem exitContextMenuItem;			    // exit menu command for context menu 
        private System.Windows.Forms.MenuItem showContextMenuItem;			    // open menu command for context menu
        private System.Windows.Forms.Form mainForm;						        // the current form we're displaying
        Process runCommand = null;

        #region Initialisation

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            if (!Properties.Settings.Default.Upgraded)
            {
                Properties.Settings.Default.Upgrade();
                Properties.Settings.Default.Upgraded = true;
            }

            Program applicationContext = new Program();
            Application.EnableVisualStyles();
            Application.Run(applicationContext);
        }

        /// <summary>
        /// This class should be created and passed into Application.Run( ... )
        /// </summary>
        public Program()
        {
            // create the notify icon and it's associated context menu
            InitializeContext();
        }

        /// <summary>
        /// Create the NotifyIcon UI, the ContextMenu for the NotifyIcon and an Exit menu item. 
        /// </summary>
        private void InitializeContext()
        {
            this.components = new System.ComponentModel.Container();
            this.mainNotifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
            this.mainNotifyIconContextMenu = new System.Windows.Forms.ContextMenu();
            this.showContextMenuItem = new System.Windows.Forms.MenuItem();
            this.exitContextMenuItem = new System.Windows.Forms.MenuItem();

            // 
            // mainNotifyIcon
            // 
            this.mainNotifyIcon.ContextMenu = this.mainNotifyIconContextMenu;
            this.mainNotifyIcon.Click += new System.EventHandler(this.mainNotifyIcon_Click);
            this.mainNotifyIcon.Icon = Properties.Resources.ApplicationIcon16x16;
            this.mainNotifyIcon.Text = "Power Triggers";
            this.mainNotifyIcon.Visible = true;

            // 
            // mainNotifyIconContextMenu
            // 
            this.mainNotifyIconContextMenu.MenuItems.AddRange(
                new MenuItem[] 
                {
                    showContextMenuItem, exitContextMenuItem 
                });


            // 
            // showContextMenuItem
            // 
            this.showContextMenuItem.Index = 0;
            this.showContextMenuItem.Text = "&Show";
            this.showContextMenuItem.DefaultItem = true;
            this.showContextMenuItem.Click += new System.EventHandler(this.showContextMenuItem_Click);

            // 
            // exitContextMenuItem
            // 
            this.exitContextMenuItem.Index = 1;
            this.exitContextMenuItem.Text = "&Exit";
            this.exitContextMenuItem.Click += new System.EventHandler(this.exitContextMenuItem_Click);

            SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
        }

        /// <summary>
        /// When the application context is disposed, dispose things like the notify icon.
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                SystemEvents.PowerModeChanged -= new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);

                if (components != null)
                {
                    components.Dispose();
                }
            }
        }

        #endregion

        #region Events
        
        private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
        {
            string strPath;
            string strFile;
            string output = "";

            // User is putting the system into standby 
            if (e.Mode == PowerModes.Suspend)
            {
                AddHistory("Suspending");

                if (Properties.Settings.Default.SuspendMethod == 1)
                {
                    if (Properties.Settings.Default.SuspendAction != null)
                    {
                        try
                        {
                            strPath = Properties.Settings.Default.SuspendAction;
                            strFile = Path.GetFileName(strPath);
                            strPath = Path.GetDirectoryName(strPath);

                            AddHistory("Running suspend action (" + strFile + ")");

                            cleanupProcess();

                            runCommand = new Process();
                            runCommand.StartInfo.FileName = Properties.Settings.Default.SuspendAction;
                            //runCommand.StartInfo.FileName = "cmd.exe";
                            //runCommand.StartInfo.Arguments = String.Format("/k \"{0}\"", Properties.Settings.Default.SuspendAction);
                            runCommand.StartInfo.WorkingDirectory = strPath;
                            runCommand.StartInfo.UseShellExecute = false;
                            runCommand.StartInfo.RedirectStandardOutput = Properties.Settings.Default.SuspendCapture;
                            runCommand.Start();
                            if (Properties.Settings.Default.SuspendCapture)
                                output = runCommand.StandardOutput.ReadToEnd();
                            runCommand.WaitForExit();

                            AddHistory("Suspend action complete");

                            if (Properties.Settings.Default.SuspendCapture)
                                AddHistory("Suspend result:\n" + output);
                        }
                        catch (Exception ex)
                        {
                            AddHistory("Suspend ERROR: " + ex.Message);
                            cleanupProcess();
                        }
                    }
                }

                if (Properties.Settings.Default.SuspendMethod == 2)
                {
                    int waitMilliseconds;
                    waitMilliseconds = Properties.Settings.Default.SuspendTerminateAfterSec * 1000;

                    Process proc;
                    proc = Process.Start(Properties.Settings.Default.SuspendAction);

                    if (waitMilliseconds > 0)
                        proc.WaitForExit(waitMilliseconds);
                    else
                        proc.WaitForExit();

                    if (!proc.HasExited)
                    {
                        try
                        {
                            proc.Kill();
                            proc.Dispose();
                            proc = null;
                        }
                        catch { }
                    }
                }

                if (Properties.Settings.Default.SuspendMethod == 3)
                {
                    int waitMilliseconds;
                    waitMilliseconds = Properties.Settings.Default.SuspendTerminateAfterSec * 1000;

                    Process proc;
                    ProcessStartInfo procInfo = new ProcessStartInfo();

                    strPath = Properties.Settings.Default.ResumeAction;
                    strFile = Path.GetFileName(strPath);
                    strPath = Path.GetDirectoryName(strPath);

                    procInfo.UseShellExecute = true;
                    procInfo.WorkingDirectory = strPath;
                    procInfo.FileName = Properties.Settings.Default.SuspendAction;
                    
                    if (Properties.Settings.Default.SuspendRunMinimised)
                        procInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Minimized;

                    proc = Process.Start(procInfo);

                    if (waitMilliseconds > 0)
                        proc.WaitForExit(waitMilliseconds);
                    else
                        proc.WaitForExit();

                    if (!proc.HasExited)
                    {
                        try
                        {
                            proc.Kill();
                            proc.Dispose();
                            proc = null;
                        }
                        catch { }
                    }
                }
            }

            // User is waking the system from standby 
            if (e.Mode == PowerModes.Resume)
            {
                AddHistory("Resuming");

                if (Properties.Settings.Default.ResumeMethod == 1)
                {
                    if (Properties.Settings.Default.ResumeAction != null)
                    {
                        try
                        {
                            strPath = Properties.Settings.Default.ResumeAction;
                            strFile = Path.GetFileName(strPath);
                            strPath = Path.GetDirectoryName(strPath);

                            AddHistory("Running resume action (" + strFile + ")");

                            cleanupProcess();

                            runCommand = new Process();
                            runCommand.StartInfo.FileName = Properties.Settings.Default.ResumeAction;
                            runCommand.StartInfo.WorkingDirectory = strPath;
                            runCommand.StartInfo.UseShellExecute = false;
                            runCommand.StartInfo.RedirectStandardOutput = Properties.Settings.Default.ResumeCapture;
                            runCommand.Start();
                            if (Properties.Settings.Default.ResumeCapture)
                                output = runCommand.StandardOutput.ReadToEnd();
                            runCommand.WaitForExit();

                            AddHistory("Resume action complete");

                            if (Properties.Settings.Default.ResumeCapture)
                                AddHistory("Resume result:\n" + output);
                        }
                        catch (Exception ex)
                        {
                            AddHistory("Resume ERROR: " + ex.Message);
                            cleanupProcess();
                        }
                    }
                }

                if (Properties.Settings.Default.ResumeMethod == 2)
                {
                    int waitMilliseconds;
                    waitMilliseconds = Properties.Settings.Default.ResumeTerminateAfterSec * 1000;

                    Process proc;
                    proc = Process.Start(Properties.Settings.Default.ResumeAction);

                    if (waitMilliseconds > 0)
                        proc.WaitForExit(waitMilliseconds);
                    else
                        proc.WaitForExit();

                    if (!proc.HasExited)
                    {
                        try
                        {
                            proc.Kill();
                            proc.Dispose();
                            proc = null;
                        }
                        catch { }
                    }
                }

                if (Properties.Settings.Default.ResumeMethod == 3)
                {
                    int waitMilliseconds;
                    waitMilliseconds = Properties.Settings.Default.ResumeTerminateAfterSec * 1000;

                    Process proc;
                    ProcessStartInfo procInfo = new ProcessStartInfo();

                    strPath = Properties.Settings.Default.ResumeAction;
                    strFile = Path.GetFileName(strPath);
                    strPath = Path.GetDirectoryName(strPath);

                    procInfo.UseShellExecute = true;
                    procInfo.WorkingDirectory = strPath;
                    procInfo.FileName = Properties.Settings.Default.ResumeAction;

                    if (Properties.Settings.Default.ResumeRunMinimised)
                        procInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Minimized;

                    proc = Process.Start(procInfo);

                    if (waitMilliseconds > 0)
                        proc.WaitForExit(waitMilliseconds);
                    else
                        proc.WaitForExit();

                    if (!proc.HasExited)
                    {
                        try
                        {
                            proc.Kill();
                            proc.Dispose();
                            proc = null;
                        }
                        catch { }
                    }
                }
            }
        }

        private void cleanupProcess()
        {
            if (runCommand != null)
            {
                try
                {
                    runCommand.Kill();
                    runCommand.Dispose();
                    runCommand = null;
                }
                catch { }
            }
        }

        /// <summary>
        /// When the open menu item is clicked, make a call to Show the form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showContextMenuItem_Click(object sender, EventArgs e)
        {
            ShowForm();
        }
        /// <summary>
        /// When the exit menu item is clicked, make a call to terminate the ApplicationContext.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitContextMenuItem_Click(object sender, EventArgs e)
        {
            ExitThread();
        }

        /// <summary>
        /// When the notify icon is clicked in the system tray, bring up the main form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mainNotifyIcon_Click(object sender, System.EventArgs e)
        {
            if (((MouseEventArgs)e).Button == MouseButtons.Left)
                ShowForm();
        }

        private void mainForm_Closed(object sender, EventArgs e)
        {
            // null out the main form so we know to create a new one.
            this.mainForm = null;
        }

        #endregion

        #region Methods

        /// <summary>
        /// This function will either create a new Form or activate the existing one, bringing the 
        /// window to front.
        /// </summary>
        private void ShowForm()
        {
            if (mainForm == null)
            {
                // create a fresh new frmMain and show it.
                mainForm = new frmMain(this);
                mainForm.TopMost = true;
                mainForm.Show();

                // hook onto the closed event so we can null out the main form...  this avoids reshowing
                // a disposed form.
                mainForm.Closed += new EventHandler(mainForm_Closed);

                mainForm.Activate();
            }
            else
            {
                // the form is currently visible, so close it
                mainForm.Close();
            }
        }

        /// <summary>
        /// If we are presently showing a mainForm, clean it up.
        /// </summary>
        protected override void ExitThreadCore()
        {
            if (mainForm != null)
            {
                // before we exit, give the main form a chance to clean itself up.
                mainForm.Close();
            }

            base.ExitThreadCore();
        }

        public void AddHistory(string historyText)
        {
            while (Properties.Settings.Default.History.Count > 20)
            {
                Properties.Settings.Default.History.RemoveAt(0);
            }
            Properties.Settings.Default.History.Add(DateTime.Now.ToLongTimeString() + ": " + historyText);
            Properties.Settings.Default.Save();
        }

        #endregion
    }
}
